package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

//spring初始化不是创建所有的Bean，只是初始化非懒加载的Bean
//实例化：是对象创建的过程。比如使用构造方法new对象，为对象在内存中分配空间。
//初始化：是为对象中的属性赋值的过程。

/*
spring Bean生命周期
class --> BeanDefinition
        --> new User()
        --> 填充属性
        --> Aware回调属性和方法
        --> 初始化 init
        --> AOP
        --> BeanPostProcessor处理器对前面所生成的对象进行加工
        --> 单例池（就是一个Map； Map<BeanName,对象> BeanName作为key）*/


//Method threw 'java.lang.StackOverflowError' exception. Cannot evaluate java.lang.Class.toString()
//对象A引用了对象B，对象B又反过来引用了对象A，导致出现了一个环形的引用链，使用toString()方法时，会不断的互相循环调用引用对象的方法，导致栈溢出。

public class demoApplicationContext {

    private static ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();;
    private static List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();;


    public demoApplicationContext(Class configclass) {
        //扫描类
        List<Class> classList = scan(configclass);
        for(Class clazz: classList){
            //if (clazz.isAnnotationPresent(Component.class)) {//isAnnotationPresent判断方法上自定义注解
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanclass(clazz);

            Component component = (Component) clazz.getAnnotation(Component.class);
            String beanName = component.value();
            if(clazz.isAnnotationPresent(Scope.class)){
                Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            }else {
                beanDefinition.setScope("singleton");//没有Scope属性则默认单例
            }

            //clazz是不是从BeanPostProcessor派生出来的，通俗的讲clazz是不是BeanPostProcessor的子类
            if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                try {
                    BeanPostProcessor bpp = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                    beanPostProcessors.add(bpp);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }

            }

            beanDefinitionMap.put(beanName,beanDefinition);
        }
        for (String beanName:beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")){
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);

            }
        }

        //扫描到之后解析这个类  Component BeanDefinition

        //生成单例 -- 。单例池

    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class beanclass = beanDefinition.getBeanclass();
        try {
            //实例化 反射
            Object bean = beanclass.getDeclaredConstructor().newInstance();
            //填充属性  不是所有都要填充
            Field[] fields = beanclass.getDeclaredFields();
            for (Field field:fields) {
                if (field.isAnnotationPresent(Autowried.class)){
                    Object userService = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(bean,userService);
                }
            }

            //Aware
            if (bean instanceof BeanNameAware){
                ((BeanNameAware)bean).setBeanName(beanName);
            }

            //***  程序员定义的逻辑 ***
            //找到存在前置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
            }

            //初始化
            //instanceof左边的对象是否是它右边的类的实例 bean有没有实现InitializingBean接口的意思
            //Object testObject = new ArrayList();  if (o instanceof Vector)  System.out.println("对象是 java.util.Vector 类的实例");
            if (bean instanceof InitializingBean){
                ((InitializingBean)bean).afterPropertiesSet();
            }

            //***  程序员定义的逻辑 ***
            // 后置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
            }

            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    private List<Class> scan(Class configclass) {
        List<Class> classList = new ArrayList<>();

        ComponentScan componentScan = (ComponentScan) configclass.getAnnotation(ComponentScan.class);
        String scanPath = componentScan.value();
        System.out.println(scanPath); //com.junhao,service 要转成能识别的目录形势
        //如何扫描类
        scanPath = scanPath.replace(".", "/");
        System.out.println(scanPath);
        ClassLoader classLoader = demoApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(scanPath);

        File file = new File(resource.getFile());
        File[] files = file.listFiles();
        //System.out.println("文件数量："+files.length);
        for (File f : files) {
            //现在需要转成com.xx.xx
            String absolutePath = f.getAbsolutePath();
            absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
            absolutePath = absolutePath.replace("\\",".");
            System.out.println(absolutePath);

            try {
                Class<?> clazz = classLoader.loadClass(absolutePath);
                classList.add(clazz);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return classList;
    }

    public Object getBean(String beanName){
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("prototype")){
            //原型
            return createBean(beanName,beanDefinition);
        }else {
            //单例
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                Object o = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,o);
                return o;
            }
            return bean;
        }
    }


}
